package com.ruubypay.biz.activiti;

import com.ruubypay.biz.mvc.api.model.request.QueryTask;
import com.ruubypay.biz.mvc.api.model.response.OutGoingDetail;
import com.ruubypay.biz.mvc.api.model.response.TaskEntity;
import com.ruubypay.biz.mvc.workflow.model.response.AuditProcessResponse;
import com.ruubypay.biz.mvc.workflow.model.response.RuntimeProcessResponse;
import com.ruubypay.biz.utils.DateUtils.DateUtil;
import com.ruubypay.biz.utils.io.resource.ResourceConverter;
import com.ruubypay.web.common.utils.StringUtils;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.activiti.image.ProcessDiagramGenerator;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 与工作流相关的通用层
 * @author chenhaiyang
 */
public class CommonProcessService {

    /**
     * 图片的base64格式的base64头
     */
    private static final String BASE_64_HEADER="data:image/jpeg;base64,";
    /**
     * 并行网关
     */
    private static final String PARALLEL_GATEWAY="parallelGateway";
    /**
     * 包含网关
     */
    private static final String INCLUSIVE_GATEWAY="inclusiveGateway";
    /**
     * 当前活动节点为用户节点
     */
    private static final String USER_TASK="userTask";
    /**
     * 当前活动节点为结束事件
     */
    private static final String END_EVENT="endEvent";
    /**
     * 已签收的任务
     */
    private static final Integer ASSIGNED_TASK=1;
    /**
     * 未签收的任务
     */
    private static final Integer UN_ASSIGNED_TASK=2;

    /**
     * 根据流程定义id获取历史活动
     * @param processEngine 流程引擎对象
     * @param processInstanceId 流程实例Id
     * @return 返回历史活动
     */
    protected List<AuditProcessResponse> getHistoicFlowList(ProcessEngine processEngine, String processInstanceId){
        //根据流程实例id获取历史活动集合列表
        List<HistoricActivityInstance> historicActivityInstances = processEngine.getHistoryService()
                .createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricActivityInstanceStartTime().asc()
                .orderByHistoricActivityInstanceEndTime().asc()
                .list();

       return Optional.ofNullable(historicActivityInstances)
                .map(List::stream)
                .orElseGet(Stream::empty)
                .map(history->buildAuditProcessResponseVoByHistory(history,processEngine))
                .collect(Collectors.toList());
    }

    /**
     * 根据流程实例对象构造运行中的流程查询相关变量返回定义类
     * @param instance 流程实例对象
     * @param processEngine 流程引擎
     * @return 返回RuntimeProcessResponse
     */
    protected RuntimeProcessResponse buildRuntimeResponseByInstance(ProcessInstance instance,ProcessEngine processEngine){

        RuntimeProcessResponse processResponse = new RuntimeProcessResponse();
        processResponse.setName(instance.getProcessDefinitionName());
        processResponse.setExcuseId(instance.getId());
        processResponse.setSuspended(instance.isSuspended());
        processResponse.setProcessInstanceId(instance.getProcessInstanceId());
        processResponse.setBusinessKey(instance.getBusinessKey());

        HistoricProcessInstance historicProcessInstance = processEngine.getHistoryService().createHistoricProcessInstanceQuery().
                processDefinitionId(instance.getProcessDefinitionId())
                .unfinished()
                .processInstanceId(instance.getProcessInstanceId())
                .singleResult();

        //流程发起人和流程发起时间
        processResponse.setStartUser(historicProcessInstance.getStartUserId());
        processResponse.setStartTime(historicProcessInstance.getStartTime());

        //当前任务节点和任务办理者
        Task task = processEngine.getTaskService().createTaskQuery()
                .executionId(instance.getId())
                .singleResult();

        processResponse.setAssignee(getTaskUsersByTaskId(processEngine,task.getId()));
        processResponse.setTaskName(task.getName());

        return processResponse;
    }

    /**
     * 根据流程实例Id获取流程跟踪图
     * @param processEngine 流程引擎对象
     * @param processInstanceId 流程实例Id
     * @return 返回Base64编码的流程图字符串。
     * @throws IOException IO异常
     */
    protected String getNowTaskPngByProcessInstanceId(ProcessEngine processEngine,String processInstanceId) throws IOException {

        HistoricProcessInstance hip = processEngine.getHistoryService().
                createHistoricProcessInstanceQuery().
                processInstanceId(processInstanceId).
                singleResult();

        //获取流程中已经执行的节点，按照执行先后顺序排序
        List<HistoricActivityInstance> hai = processEngine.getHistoryService()
                .createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricActivityInstanceId().asc().list();

        // 构造已执行的节点ID集合
        List<String> executedActivityIdList = hai.stream()
                .map(HistoricActivityInstance::getActivityId)
                .collect(Collectors.toList());

        // 获取bpmnModel
        BpmnModel bpmnModel = processEngine.getRepositoryService().getBpmnModel(hip.getProcessDefinitionId());
        // 获取流程已发生流转的线ID集合
        List<String> flowIds = getExecutedFlows(bpmnModel, hai);
        ProcessDiagramGenerator processDiagramGenerator = processEngine
                .getProcessEngineConfiguration()
                .getProcessDiagramGenerator();
        //使用默认配置获得流程图表生成器，并生成追踪图片字符流
        InputStream imageStream = processDiagramGenerator.generateDiagram(bpmnModel, "png", executedActivityIdList, flowIds, "宋体", "微软雅黑", "黑体", null, 2.0);
        return BASE_64_HEADER+ ResourceConverter.converterstreamAsBase64Str(imageStream);
    }

    /**
     * 查询待签收的任务
     * @param queryTaskList 查询任务实体
     * @return 返回结果
     */
    protected List<TaskEntity> getUnAssignedTask(ProcessEngine processEngine,QueryTask queryTaskList) {
        TaskQuery toClaimQuery = processEngine.getTaskService()
                .createTaskQuery()
                .taskCandidateUser(queryTaskList.getUserName())
                .includeProcessVariables()
                .active();

        if (queryTaskList.getBeginDate() != null){
            toClaimQuery.taskCreatedAfter(queryTaskList.getBeginDate());
        }
        if (queryTaskList.getEndDate() != null){
            toClaimQuery.taskCreatedBefore(queryTaskList.getEndDate());
        }
        List<Task> needClaimTask = toClaimQuery.list();
        return Optional.ofNullable(needClaimTask)
                .map(List::stream)
                .orElseGet(Stream::empty)
                .map(task->getTaskEntityByTask(processEngine,task,UN_ASSIGNED_TASK))
                .collect(Collectors.toList());
    }

    /**
     * 获取已签收的任务
     * @param queryTaskList 任务查询对象
     * @return 返回已签收的任务列表
     */
    protected List<TaskEntity> getAssignedTask(ProcessEngine processEngine,QueryTask queryTaskList) {
        //查询已经签收的任务
        TaskQuery todoTaskQuery = processEngine.getTaskService()
                .createTaskQuery()
                .taskAssignee(queryTaskList.getUserName())
                .active()
                .includeProcessVariables();

        if (queryTaskList.getBeginDate() != null){
            todoTaskQuery.taskCreatedAfter(queryTaskList.getBeginDate());
        }
        if (queryTaskList.getEndDate() != null){
            todoTaskQuery.taskCreatedBefore(queryTaskList.getEndDate());
        }

        List<Task> assigneTask = todoTaskQuery.list();
        return Optional.ofNullable(assigneTask)
                .map(List::stream)
                .orElseGet(Stream::empty)
                .map(task->getTaskEntityByTask(processEngine,task,ASSIGNED_TASK))
                .collect(Collectors.toList());
    }

    /**
     * 构建返回对象（连线实体）
     * @param pvmTransition 连线信息
     * @return 返回 OutGoingDetail 连线实体
     */
    protected OutGoingDetail buildOutGoingDetail(PvmTransition pvmTransition) {
        OutGoingDetail detail = new OutGoingDetail();

        String name = (String) Optional.ofNullable(pvmTransition.getProperty("name"))
                .orElse("提交");
        detail.setName(name);
        String condition=(String) pvmTransition.getProperty("conditionText");
        //将条件中的双引号换成单引号,去掉${},去掉空格
        if(StringUtils.isNotBlank(condition)){
            condition = condition
                    .replaceAll("\"","'")
                    .replaceAll("\\$\\{","")
                    .replaceAll("}","")
                    .replaceAll(" ","");
        }
        detail.setCondition(condition);

        return detail;
    }

    /**
     * 获取任务要办理的连线。比如，用于页面生成按钮，比如：提交 或者（提交|驳回），每一个动作都是一个连线
     * @param activityImpl 当前的活动
     * @return 返回连线的集合
     */
    protected List<PvmTransition> getOutGoingLines(ActivityImpl activityImpl) {
        //如果下一个节点是用户节点，则获取连线
        if(nextIsUserTask(activityImpl)){
            return activityImpl.getOutgoingTransitions();
            //如果是网关，则获取网关后的连线，则为正确的连线
        }else{
            List<PvmTransition> pvmList = activityImpl.getOutgoingTransitions();
            if(pvmList.size()==0){
                return new ArrayList<>();
            }
            PvmActivity ac = pvmList.get(0).getDestination();
            return ac.getOutgoingTransitions();
        }
    }

    /**
     * 根据历史任务构建TaskEntity信息
     * @param historyTask 根据历史任务构建TaskEntity信息
     * @return 返回任务信息
     */
    protected TaskEntity buildTaskEntityByHistory(ProcessEngine processEngine,HistoricTaskInstance historyTask) {

        TaskEntity entity = new TaskEntity();
        HistoricProcessInstance instance = processEngine.getHistoryService()
                .createHistoricProcessInstanceQuery()
                .processInstanceId(historyTask.getProcessInstanceId())
                .singleResult();

        entity.setProcessName(instance.getProcessDefinitionName());
        entity.setProcessInstanceId(instance.getId());
        entity.setTaskId(historyTask.getId());

        entity.setStartUserName(instance.getStartUserId());
        entity.setBusinessKey(instance.getBusinessKey());
        entity.setName(historyTask.getName());
        entity.setCreateTime(historyTask.getCreateTime());

        entity.setFormKey(historyTask.getFormKey());
        entity.setProcessVariables(historyTask.getProcessVariables());

        //设置流程的title，如果为空，则默认取流程定义的名称
        String title = (String) Optional.ofNullable(historyTask.getProcessVariables())
                .orElseGet(HashMap::new).get("title");
        entity.setTitle(Optional.ofNullable(title).orElse(instance.getProcessDefinitionName()));

        entity.setType(ASSIGNED_TASK);
        return entity;
    }

    /**
     * 根据历史活动实例构造历史活动查询VO类
     * @param history 历史活动实例
     * @param processEngine 流程引擎对象
     * @return 返回审批流程历史活动响应VO类
     */
    private AuditProcessResponse buildAuditProcessResponseVoByHistory(HistoricActivityInstance history, ProcessEngine processEngine){
        AuditProcessResponse processResponseVo = new AuditProcessResponse();

        processResponseVo.setActivityName(history.getActivityName());
        processResponseVo.setStartTime(history.getStartTime());
        processResponseVo.setEndTime(history.getEndTime());
        processResponseVo.setAssignee(getTaskUsersByTaskId(processEngine,history.getTaskId()));
        processResponseVo.setUseTime(DateUtil.getTimeAsString(history.getDurationInMillis()));
        //查询历史批注,我们只支持提交一次批注，只能做一次批注
        List<Comment> comments = processEngine.getTaskService().getTaskComments(history.getTaskId());
        if(comments!=null&&comments.size()>0){
            processResponseVo.setMessage(comments.get(0).getFullMessage());
        }
        return processResponseVo;
    }

    /**
     * 根据任务id获取当前所有节点的任务办理人
     * 已经办理的任务，则取getAssignee,未办理的任务，则是（任务人和候选人）
     * @param taskId taskId
     * @return 返回任务所有办理人
     */
    private String getTaskUsersByTaskId(ProcessEngine processEngine,String taskId){

        if(StringUtils.isBlank(taskId)){
            return "";
        }

        Task task = processEngine.getTaskService().createTaskQuery()
                .active()
                .taskId(taskId)
                .singleResult();

        if(task!=null){

            return Optional.ofNullable(task.getAssignee())
                    .orElseGet(()->getCandidateUser(taskId,processEngine));
        }else{

            return processEngine.getHistoryService()
                    .createHistoricTaskInstanceQuery()
                    .taskId(taskId)
                    .singleResult().getAssignee();
        }
    }

    /**
     * 如果是组任务，查询所有组候选人
     * @param taskId 任务Id
     * @param processEngine 流程引擎对象
     * @return 返回字符串
     */
    private String getCandidateUser(String taskId,ProcessEngine processEngine) {
        return Optional.ofNullable(processEngine.getTaskService().getIdentityLinksForTask(taskId))
                .map(List::stream)
                .orElseGet(Stream::empty)
                .map(IdentityLink::getUserId)
                .collect(Collectors.joining(","));
    }

    /**
     * 判断下一个活动是否是userTask或者结束事件,如果是，则返回true 否则，返回false
     * @param activityImpl 当前活动
     * @return 下一个节点是否是userTask或者是结束事件;
     */
    private boolean nextIsUserTask(ActivityImpl activityImpl) {

        List<PvmTransition> outTransitions = activityImpl.getOutgoingTransitions();

        return outTransitions.size()>1
                ||outTransitions.size()==0
                ||Arrays.asList(USER_TASK,END_EVENT).contains(String.valueOf(outTransitions.get(0).getProperty("type")));

    }

    /**
     * 获取已经发生流转的线的id的集合（算法直接摘抄自网络）
     * 一：遍历已完成的活动实例，从每个实例的outgoingFlows中找到已执行的
     * 二：遍历outgoingFlows并找到已流转的
     * 满足如下条件任务已流转：
     *  1.当前节点是并行网关或包含网关，则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
     *  2.当前节点是以上两种类型之外的，通过outgoingFlows查找到的时间最近的流转节点视为有效流转
     * @param bpmnModel bpmn模型对象
     * @param historicActivityInstances 流程中已经执行的节点
     * @return 返回 流程中已经执行的节点
     */
    private List<String> getExecutedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {

        List<String> flowIdList = new ArrayList<>();

        List<FlowNode> historicFlowNodeList = historicActivityInstances.stream()
                .map(historinstance->(FlowNode) bpmnModel.getMainProcess().getFlowElement(historinstance.getActivityId()))
                .collect(Collectors.toCollection(LinkedList::new));

        //一
        historicActivityInstances.stream()
                .filter(historinstance->historinstance.getEndTime()!=null)
                .collect(Collectors.toCollection(LinkedList::new))
                .forEach(currentActivityInstance->{

                    List<SequenceFlow> sequenceFlowList= ((FlowNode) bpmnModel.getMainProcess()
                            .getFlowElement(currentActivityInstance.getActivityId()))
                            .getOutgoingFlows();
                    //二
                    if(PARALLEL_GATEWAY.equals(currentActivityInstance.getActivityType())
                            ||INCLUSIVE_GATEWAY.equals(currentActivityInstance.getActivityType())) {

                        Optional.ofNullable(sequenceFlowList)
                                .map(List::stream)
                                .orElseGet(Stream::empty)
                                .filter(sequenceFlow->
                                        historicFlowNodeList.contains(getFlowNodeBySequenceFlow(sequenceFlow,bpmnModel)))
                                .map(SequenceFlow::getId)
                                .forEach(flowIdList::add);
                    }else{

                        Optional.ofNullable(sequenceFlowList)
                                .map(List::stream)
                                .orElseGet(Stream::empty)
                                .filter(sequenceFlow->contailssequenceFlow(sequenceFlow,historicActivityInstances))
                                .map(SequenceFlow::getId)
                                .forEach(flowIdList::add);
                    }
                });

        return flowIdList;
    }

    /**
     * 判断连线是否包含在历史活动流程中
     * @param sequenceFlow 连线
     * @param historicActivityInstances 历史活动流程列表
     * @return 返回是否包含
     */
    private boolean contailssequenceFlow(SequenceFlow sequenceFlow, List<HistoricActivityInstance> historicActivityInstances) {

        return historicActivityInstances
                .stream()
                .filter(historicActivityInstance->historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef()))
                .count()>0;
    }

    /**
     * 根据 sequenceFlow获取 FlowNode;
     * @param sequenceFlow 连线
     * @param bpmnModel bpmn模型
     * @return 返回 FlowNode
     */
    private FlowNode getFlowNodeBySequenceFlow(SequenceFlow sequenceFlow,BpmnModel bpmnModel){
        return (FlowNode)bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef());
    }

    /**
     * 根据Task封装TaskEntity
     * @param task task任务
     * @return 返回TaskEntity
     */
    private TaskEntity getTaskEntityByTask(ProcessEngine processEngine,Task task,Integer type) {
        TaskEntity entity = new TaskEntity();
        ProcessInstance instance = processEngine.getRuntimeService().createProcessInstanceQuery()
                .processInstanceId(task.getProcessInstanceId()).singleResult();
        entity.setProcessName(instance.getProcessDefinitionName());
        entity.setProcessInstanceId(instance.getProcessInstanceId());
        entity.setTaskId(task.getId());

        HistoricProcessInstance historicProcessInstance  = processEngine.getHistoryService()
                .createHistoricProcessInstanceQuery()
                .unfinished()
                .processInstanceId(task.getProcessInstanceId())
                .singleResult();
        entity.setStartUserName(historicProcessInstance.getStartUserId());
        entity.setBusinessKey(instance.getBusinessKey());
        entity.setName(task.getName());
        entity.setCreateTime(task.getCreateTime());

        entity.setFormKey(task.getFormKey());
        entity.setProcessVariables(task.getProcessVariables());

        //设置流程的title，如果为空，则默认取流程定义的名称
        String title = (String) Optional.ofNullable(task.getProcessVariables())
                .orElseGet(HashMap::new).get("title");
        entity.setTitle(Optional.ofNullable(title).orElse(instance.getProcessDefinitionName()));

        entity.setType(type);
        return entity;
    }
}
